package dungeon;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.math.BigDecimal;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import temperature.Temperature;
import dungeon.CompositeDungeon.CompositDungeonIterator;
import dungeonElements.Door;
import dungeonElements.NormalSquare;
import dungeonElements.Rock;
import dungeonElements.Square;
import dungeonElements.TeleportationSquare;
import dungeonElements.TransparentSquare;
import dungeonElements.Wall;
import dungeonTools.Coordinate;
import dungeonTools.Direction;
import dungeonTools.RockWith200TemperatureConstraint;

/**
 * @author Tom Jacobs & Ewoud De Moor
 * @version 3.0
 */
public class DungeonTest {

	public static CompositeDungeon<Square> dungeon;
	public static TeleportationSquare square1;
	public static TeleportationSquare square2;
	public static TeleportationSquare square3;
	public static TeleportationSquare square4;
	public static TeleportationSquare square5;
	public static TransparentSquare transparentSquare1;
	public static TransparentSquare transparentSquare2;
	public static TransparentSquare transparentSquare3;
	public static TransparentSquare transparentSquare4;
	public static TransparentSquare transparentSquare5;
	
	@Before
	public void setUp() throws Exception {
		dungeon = new CompositeDungeon<Square>(50,50,50);
		dungeon.addDungeon(new Level<TeleportationSquare>(50,50), new Coordinate(0,0,0));
		dungeon.addDungeon(new Level<TeleportationSquare>(50,50), new Coordinate(0,0,1));
		dungeon.addDungeon(new Level<TeleportationSquare>(50,50), new Coordinate(0,0,2));
		square1 = new NormalSquare(new Temperature(BigDecimal.valueOf(100)),50,false,false,false,false,false,true);
		square2 = new NormalSquare(new Temperature(BigDecimal.valueOf(0)),50,false,false,false,false,false,true);
		square3 = new NormalSquare(new Temperature(BigDecimal.valueOf(50)),50,false,false,false,false,false,true);
		square4 = new NormalSquare(new Temperature(BigDecimal.valueOf(50)),50,false,false,false,false,false,true);
		square5 = new NormalSquare(new Temperature(BigDecimal.valueOf(50)),50,false,false,false,false,false,true);
		transparentSquare1 = new TransparentSquare(new Temperature(BigDecimal.valueOf(50)),50,Direction.FLOOR,false);
		transparentSquare2 = new TransparentSquare(new Temperature(BigDecimal.valueOf(50)),50,Direction.WEST,true);
		transparentSquare3 = new TransparentSquare(new Temperature(BigDecimal.valueOf(50)),50,Direction.WEST,true);
		transparentSquare4 = new TransparentSquare(new Temperature(BigDecimal.valueOf(50)),50,Direction.WEST,true);
		transparentSquare5 = new TransparentSquare(new Temperature(BigDecimal.valueOf(50)),50,Direction.WEST,true);
		((Door)transparentSquare2.getBorder(Direction.WEST)).closeDoor();
		((Door)transparentSquare3.getBorder(Direction.WEST)).closeDoor();
		((Door)transparentSquare4.getBorder(Direction.WEST)).closeDoor();
		((Door)transparentSquare5.getBorder(Direction.WEST)).closeDoor();		
		
	}

	@After
	public void tearDown() throws Exception {
	}
	
	@Test
	public void expandDungeon_legal(){
		assertEquals(50,dungeon.getXSize());
		assertEquals(50,dungeon.getYSize());
		assertEquals(50,dungeon.getZSize());
		dungeon.setXSize(100);
		assertEquals(100,dungeon.getXSize());
		assertEquals(50,dungeon.getYSize());
		assertEquals(50,dungeon.getZSize());
		dungeon.setYSize(100);
		assertEquals(100,dungeon.getXSize());
		assertEquals(100,dungeon.getYSize());
		assertEquals(50,dungeon.getZSize());
		dungeon.setZSize(100);
		assertEquals(100,dungeon.getXSize());
		assertEquals(100,dungeon.getYSize());
		assertEquals(100,dungeon.getZSize());
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void expandDungeon_X_exception(){
		assertEquals(50,dungeon.getXSize());
		dungeon.setXSize(10);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void expandDungeon_Y_exception(){
		assertEquals(50,dungeon.getYSize());
		dungeon.setYSize(10);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void expandDungeon_Z_exception(){
		assertEquals(50,dungeon.getZSize());
		dungeon.setZSize(10);
	}
	
	@Test
	public void checkIfSquareInSameRoom_legal(){
		assertTrue(dungeon.canHaveSquareAtCoordinate(square1, new Coordinate(0,1,0)));
		assertTrue(dungeon.canHaveSquareAtCoordinate(square2, new Coordinate(0,2,0)));
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertTrue(dungeon.isSquareInSameRoom(square1, square2));
		assertTrue(dungeon.isSquareInSameRoom(square2, square1));
	}
	
	@Test
	public void checkIfSquareInSameRoom_FirstSquare(){
		dungeon.addSquare(square1, new Coordinate(0,2,0));
		assertFalse(dungeon.isSquareInSameRoom(square1, square2));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void checkIfSquareInSameRoom_SecondSquareException(){
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertTrue(dungeon.isSquareInSameRoom(square1, square2));
	}
	
	@Test
	public void checkIfSquareInSameRoom_WallBetween(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		square1.buildWall(Direction.NORTH);
		assertFalse(dungeon.isSquareInSameRoom(square1, square2));
		assertFalse(dungeon.isSquareInSameRoom(square2, square1));
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
	}
	
	@Test
	public void getEnclosingRoom(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertEquals(2,dungeon.getEnclosingRoom(square1).size());
		assertEquals(2,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square2));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
	}
	
	@Test
	public void getDungeonContainingSquare(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		assertSame(Dungeon.getDungeonContainingSquare(square1),dungeon.getChildDungeonContainingCoordinate(new Coordinate(0,1,0)));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void getDungeonContainingSquare_Exception(){
		assertSame(Dungeon.getDungeonContainingSquare(square1),dungeon);
	}
	
	@Test
	public void getSquareAtCoordinate(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		assertSame(square1,dungeon.getSquareAtCoordinate(new Coordinate(0,1,0)));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void getSquareAtCoordinate_Exception(){
		assertSame(square1,dungeon.getSquareAtCoordinate(new Coordinate(0,1,0)));
	}
	@Test
	public void getCoordinateOfSquare(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		assertEquals(new Coordinate(0,1,0),dungeon.getCoordinateOfSquare(square1));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void getCoordinateOfSquare_Exception(){
		assertEquals(new Coordinate(0,1,0),dungeon.getCoordinateOfSquare(square1));
	}
	
	@Test
	public void contains(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		assertTrue(dungeon.contains(new Coordinate(0,1,0)));
		assertTrue(dungeon.contains(square1));
	}
	
	@Test
	public void canHaveAsSquareAtCoordinate(){
		assertFalse(dungeon.canHaveSquareAtCoordinate(square1, new Coordinate(0,0,0)));
		assertFalse(dungeon.canHaveSquareAtCoordinate(null, new Coordinate(0,0,0)));
		assertFalse(dungeon.canHaveSquareAtCoordinate(square1,null));
		assertTrue(dungeon.canHaveSquareAtCoordinate(square1, new Coordinate(0,1,1)));
		square1.setSlipperySurface(true);
		assertFalse(dungeon.canHaveSquareAtCoordinate(square1, new Coordinate(0,1,1)));
		square1.setSlipperySurface(false);
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		assertFalse(dungeon.canHaveSquareAtCoordinate(square1, new Coordinate(0,1,1)));
		assertFalse(dungeon.canHaveSquareAtCoordinate(square2, new Coordinate(0,1,1)));
	}
	
	@Test(expected = IllegalStateException.class)
	public void squareSlipperySurface_Exception(){
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		square1.setSlipperySurface(true);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void addSquare_Exception(){
		dungeon.addSquare(square1, new Coordinate(0,0,0));
	}
	
	@Test
	public void isSquareInsideAnyDungeon(){
		assertFalse(Dungeon.isSquareInsideAnyDungeon(square1));
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		assertTrue(Dungeon.isSquareInsideAnyDungeon(square1));	
	}
	
	@Test
	public void removeStatic(){
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		assertTrue(Dungeon.isSquareInsideAnyDungeon(square1));
		assertTrue(dungeon.contains(square1));
		assertTrue(dungeon.contains(new Coordinate(0,1,1)));
		Dungeon.remove(square1);
		assertFalse(Dungeon.isSquareInsideAnyDungeon(square1));
		assertFalse(dungeon.contains(new Coordinate(0,1,1)));
	}
	
	@Test
	public void remove(){
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		assertTrue(dungeon.contains(new Coordinate(0,1,1)));
		dungeon.remove(new Coordinate(0,1,1));
		assertFalse(dungeon.contains(new Coordinate(0,1,1)));
	}
	
	@Test
	public void remove_squareTermination(){
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		dungeon.remove(new Coordinate(0,1,1));
		assertTrue(square1.isTerminated());
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void remove_Exception(){
		dungeon.remove(new Coordinate(0,1,1));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void removeStatic_Exception(){
		Dungeon.remove(square1);
	}
	
	@Test
	public void roomTerminationTest(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		Dungeon.remove(square1);
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
	}
	
	@Test
	public void doorOpen(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertEquals(2,dungeon.getEnclosingRoom(square1).size());
		square1.buildDoor(Direction.NORTH);
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
		((Door) square1.getBorder(Direction.NORTH)).openDoor();
		assertEquals(2,dungeon.getEnclosingRoom(square1).size());
		((Door) square1.getBorder(Direction.NORTH)).closeDoor();
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
	}
	
	@Test
	public void breakWall(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		square1.buildWall(Direction.NORTH);
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
		square1.breakBorder(Direction.NORTH);
		assertEquals(2,dungeon.getEnclosingRoom(square1).size());
		assertEquals(2,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square2));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square1));
		square1.buildDoor(Direction.NORTH);
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
	}
	
	@Test
	public void terminateWall(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		square1.buildWall(Direction.NORTH);
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
		square1.getBorder(Direction.NORTH).terminate();
		assertEquals(2,dungeon.getEnclosingRoom(square1).size());
		assertEquals(2,dungeon.getEnclosingRoom(square2).size());
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square1));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square2));
		assertTrue(dungeon.getEnclosingRoom(square1).contains(square2));
		assertTrue(dungeon.getEnclosingRoom(square2).contains(square1));
		square1.buildDoor(Direction.NORTH);
		assertEquals(1,dungeon.getEnclosingRoom(square1).size());
		assertEquals(1,dungeon.getEnclosingRoom(square2).size());
	}
	
	@Test
	public void squareWallUpgradingSouthToNorth(){
		square2.buildWall(Direction.SOUTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Wall);
	}
	
	@Test
	public void squareDoorUpgradingSouthToNorth(){
		square2.buildDoor(Direction.SOUTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Door);
	}
	
	@Test
	public void squareWallUpgradingNorthToSouth(){
		square1.buildWall(Direction.NORTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Wall);
	}
	
	@Test
	public void squareDoorUpgradingNorthToSouth(){
		square1.buildDoor(Direction.NORTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Door);
	}
	
	@Test
	public void squareUpgradeDoorToWall(){
		square1.buildDoor(Direction.NORTH);
		square2.buildWall(Direction.SOUTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Wall);
	}
	
	@Test
	public void squareUpgradeNullToDoor(){
		square1.buildDoor(Direction.NORTH);
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		assertSame(square1.getBorder(Direction.NORTH),square2.getBorder(Direction.SOUTH));
		assertTrue(square1.getBorder(Direction.NORTH) instanceof Door);
	}
	
	@Test
	public void detectingComplexRoomShape(){
		Square squareAt100 = new NormalSquare(new Temperature(BigDecimal.valueOf(50)),50,false,false,false,false,false,true);
		Square squareAt110 = new NormalSquare(new Temperature(BigDecimal.valueOf(10)),50,false,false,false,false,false,true);
		Square squareAt101 = new NormalSquare(new Temperature(BigDecimal.valueOf(20)),50,false,false,false,false,true,false);
		Square squareAt010 = new NormalSquare(new Temperature(BigDecimal.valueOf(30)),50,false,false,false,false,false,true);
		Square squareAt011 = new NormalSquare(new Temperature(BigDecimal.valueOf(40)),50,false,false,false,false,true,false);
//		Square squareAt001 = new TeleportationSquare(new Temperature(BigDecimal.valueOf(001d)),50,false,false,false,false,true,false);
//		dungeon.addSquare(squareAt001, new Coordinate(0,0,1));
		dungeon.addSquare(squareAt011, new Coordinate(0,1,1));
		dungeon.addSquare(squareAt101, new Coordinate(1,0,1));
		dungeon.addSquare(squareAt100, new Coordinate(1,0,0));
		dungeon.addSquare(squareAt110, new Coordinate(1,1,0));
//		squareAt001.setSlipperySurface(true);
		squareAt010.setSlipperySurface(true);
		assertFalse(dungeon.canHaveSquareAtCoordinate(squareAt010, new Coordinate(0,1,0)));
		squareAt010.setSlipperySurface(false);
//		squareAt001.setSlipperySurface(false);
		dungeon.addSquare(squareAt010, new Coordinate(0,1,0));
		HashSet<Square> hset = dungeon.getEnclosingRoom(squareAt011);
		assertEquals(5,hset.size());
		assertEquals(new Temperature(BigDecimal.valueOf(30)), squareAt011.getTemperature());
	}
	
	@Test
	public void changingSquareDataRefreshesRoomData(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		square1.setTemperature(new Temperature(BigDecimal.valueOf(100)));
		assertEquals(square1.getTemperature(), square2.getTemperature());
		assertEquals(new Temperature(BigDecimal.valueOf(75)),square1.getTemperature());
		square2.setHumidity(100);
		assertEquals(square1.getHumidity(), square2.getHumidity(),0.01d);
		assertEquals(75,square1.getHumidity(),0.01d);
	}
	
	@Test
	public void teleportWithinDungeonDifferentRooms(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,0,1));
		HashSet<Square> square1Room = dungeon.getEnclosingRoom(square1);
		HashSet<Square> square2Room = dungeon.getEnclosingRoom(square2);
		assertEquals(square1Room.size(), square2Room.size());
		assertEquals(1,square1Room.size());
		square1.setTeleportationDestination(square2);
		HashSet<Square> square1TeleportationArea = dungeon.getAllReachableSquares(square1);
		HashSet<Square> square2TeleportationArea = dungeon.getAllReachableSquares(square2);
		assertFalse(square1TeleportationArea.equals(square2TeleportationArea));
		assertEquals(2,square1TeleportationArea.size());
		assertTrue(square1TeleportationArea.contains(square1));
		assertTrue(square1TeleportationArea.contains(square2));
		assertEquals(1,square2TeleportationArea.size());
		square2.setTeleportationDestination(square1);
		square1TeleportationArea = dungeon.getAllReachableSquares(square1);
		square2TeleportationArea = dungeon.getAllReachableSquares(square2);
		assertEquals(square1TeleportationArea,square2TeleportationArea);
		assertEquals(2,square1TeleportationArea.size());
		assertTrue(square1TeleportationArea.contains(square1));
		assertTrue(square1TeleportationArea.contains(square2));
		square1Room = dungeon.getEnclosingRoom(square1);
		square2Room = dungeon.getEnclosingRoom(square2);
		assertEquals(1,square1Room.size());
		assertTrue(square1Room.contains(square1));
		assertEquals(1,square2Room.size());
		assertTrue(square2Room.contains(square2));
	}
	
	@Test
	public void teleportBetweenDungeons(){
		dungeon.addSquare(square1, new Coordinate(1, 0, 0));
		CompositeDungeon<Square> dungeon2 = new CompositeDungeon<Square>(50,50,50);
		dungeon2.addDungeon(new Level<Square>(50,50), Coordinate.ORIGIN);
		dungeon2.addSquare(square2, new Coordinate(1,0,0));
		HashSet<Square> square1Room = dungeon.getEnclosingRoom(square1);
		HashSet<Square> square2Room = dungeon2.getEnclosingRoom(square2);
		assertEquals(square1Room.size(), square2Room.size());
		assertEquals(1,square1Room.size());
		square1.setTeleportationDestination(square2);
		HashSet<Square> square1TeleportationArea = dungeon.getAllReachableSquares(square1);
		HashSet<Square> square2TeleportationArea = dungeon2.getAllReachableSquares(square2);
		assertFalse(square1TeleportationArea.equals(square2TeleportationArea));
		assertEquals(2,square1TeleportationArea.size());
		assertTrue(square1TeleportationArea.contains(square1));
		assertTrue(square1TeleportationArea.contains(square2));
		assertEquals(1,square2TeleportationArea.size());
		square2.setTeleportationDestination(square1);
		square1TeleportationArea = dungeon.getAllReachableSquares(square1);
		square2TeleportationArea = dungeon2.getAllReachableSquares(square2);
		assertEquals(square1TeleportationArea,square2TeleportationArea);
		assertEquals(2,square1TeleportationArea.size());
		assertTrue(square1TeleportationArea.contains(square1));
		assertTrue(square1TeleportationArea.contains(square2));
		square1Room = dungeon.getEnclosingRoom(square1);
		square2Room = dungeon2.getEnclosingRoom(square2);
		assertEquals(1,square1Room.size());
		assertTrue(square1Room.contains(square1));
		assertEquals(1,square2Room.size());
		assertTrue(square2Room.contains(square2));
	}
	
	@Test
	public void cyclicTeleportationSequence(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(0,1,0));
		dungeon.addSquare(square3, new Coordinate(2,1,0));
		square1.setTeleportationDestination(square2);
		square2.setTeleportationDestination(square3);
		assertEquals(3,dungeon.getAllReachableSquares(square1).size());
		assertEquals(2,dungeon.getAllReachableSquares(square2).size());
		assertEquals(1,dungeon.getAllReachableSquares(square3).size());
		square3.setTeleportationDestination(square1);
		assertEquals(dungeon.getAllReachableSquares(square1),dungeon.getAllReachableSquares(square2));
		assertEquals(dungeon.getAllReachableSquares(square3),dungeon.getAllReachableSquares(square2));
		assertEquals(3,dungeon.getAllReachableSquares(square1).size());
	}
	
	@Test
	public void iteratorTest_CompositDungeon_LegalCase(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,0,1));
		assertTrue(square1.getBorder(Direction.CEILING) instanceof Wall);
		Iterator<Square> it = dungeon.iterator();
		int squareCount = 0;
		while(it.hasNext()){
			Square square = it.next();
			if((square == square1) || (square == square2))
				squareCount++;
		}
		assertEquals(2,squareCount);
		it = dungeon.iterator();
		it.next();
		it.remove();
		assertFalse(dungeon.contains(new Coordinate(1,0,0)) && dungeon.contains(new Coordinate(1,0,1)));
		Square square = it.next();
		assertTrue(square.equals(square1) ^ square.equals(square2));
	}
	
	@Test
	public void iteratorTest_CompositDungeon_implicitLegalCase(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,0,1));
		assertTrue(square1.getBorder(Direction.CEILING) instanceof Wall);
		int squareCount = 0;
		for(Square square : dungeon){
			if((square == square1) || (square == square2))
				squareCount++;
		}
		assertEquals(2,squareCount);
	}
	
	@Test(expected = NoSuchElementException.class)
	public void iteratorTest_CompositDungeon_NoSuchElementException(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		Iterator<Square> it = dungeon.iterator();
		it.next();
		it.next();
	}
	@Test
	public void iteratorTest_CompositDungeon_NoSuchElementException_FALSE(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		Iterator<Square> it = dungeon.iterator();
		it.next();
	}
	
	@Test(expected = ConcurrentModificationException.class)
	public void iteratorTest_CompositDungeon_ConcurrentModification_Remove(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		Iterator<Square> it = dungeon.iterator();
		dungeon.remove(new Coordinate(1,0,0));
		it.next();
	}
	
	@Test(expected = ConcurrentModificationException.class)
	public void iteratorTest_CompositDungeon_ConcurrentModification_Add(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		Iterator<Square> it = dungeon.iterator();
		dungeon.addSquare(square2, new Coordinate(1,0,1));
		it.next();
	}
	
	@Test(expected = IllegalStateException.class)
	public void iteratorTest_CompositDungeon_IllegalStateException(){
		Iterator<Square> it = dungeon.iterator();
		it.remove();
	}
	
	@Test(expected = IllegalStateException.class)
	public void iteratorTest_CompositDungeon_IllegalStateException_Element(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		Iterator<Square> it = dungeon.iterator();
		it.remove();
		it.remove();
	}
	
	@Test
	public void iteratorTest_CompositDungeon_2Iterators(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,1,0));
		dungeon.addSquare(square3, new Coordinate(0,1,0));
		dungeon.addSquare(square4, new Coordinate(1,2,0));
		dungeon.addSquare(square5, new Coordinate(0,2,0));
		Iterator<Square> it1 = dungeon.iterator();
		Iterator<Square> it2 = dungeon.iterator();
		for(int i = 0; i < 5; i++){
			it1.next();
			it2.next();
		}
		assertFalse(it1.hasNext());
		assertFalse(it2.hasNext());
	}
	
	@Test
	public void iteratorTest_Level_LegalCase(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,1,0));
		Dungeon<? extends Square> dung = dungeon.getChildDungeonContainingCoordinate(new Coordinate(1,0,0));
		Iterator<? extends Square> it = dung.iterator();
		it.next();
		it.remove();
		assertTrue(it.hasNext());
		it.next();
		assertFalse(it.hasNext());
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Test
	public void constraintTest(){
		CompositeDungeon<Square> dungeon = new CompositeDungeon<Square>(50, 50, 50);
		dungeon.addDungeon(new Level(50,50), Coordinate.ORIGIN);
		Rock rock1 = new Rock();
		Rock rock2 = new Rock();
		dungeon.addSquare(rock1, new Coordinate(1,0,0));
		dungeon.addSquare(rock2, new Coordinate(1,1,0));
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		square1.setTemperature(new Temperature(BigDecimal.valueOf(600)));
		Iterator<? extends Square> it = dungeon.iterator();
		HashSet<Square> set =((CompositDungeonIterator) it).getAllSquaresMatchingConstraint(new RockWith200TemperatureConstraint());
		assertEquals(2, set.size());
		assertTrue(set.contains(rock1));
		assertTrue(set.contains(rock2));
		Dungeon.remove(rock1);
		set =((CompositDungeonIterator) it).getAllSquaresMatchingConstraint(new RockWith200TemperatureConstraint());
		assertEquals(1, set.size());
		assertFalse(set.contains(rock1));
		assertTrue(set.contains(rock2));
		
	}
	
	@Test
	public void isSquareReachable_oneSingleRoom(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(1,1,0));
		dungeon.addSquare(square3, new Coordinate(0,1,0));
		dungeon.addSquare(square4, new Coordinate(1,2,0));
		dungeon.addSquare(square5, new Coordinate(0,2,0));
		dungeon.addSquare(transparentSquare1, new Coordinate(2,1,0));
		assertTrue(dungeon.isSquareReachable(square1, transparentSquare1));
		assertTrue(dungeon.isSquareReachable(square1, square1));
		assertEquals(6,dungeon.getAllReachableSquares(square1).size());
		assertEquals(dungeon.getAllReachableSquares(square1), dungeon.getAllReachableSquares(square3));
	}
	
	@Test
	public void isSquareReachable_multipleRooms_oneSquareNotInAnyDungeon(){
		dungeon.addSquare(square1, new Coordinate(1,0,0));
		dungeon.addSquare(square2, new Coordinate(0,1,0));
		assertFalse(dungeon.isSquareReachable(square1, square2));
		assertFalse(dungeon.isSquareReachable(square2, square1));
		square1.setTeleportationDestination(square2,square3);
		assertTrue(dungeon.isSquareReachable(square1, square2));
		assertFalse(dungeon.isSquareReachable(square2, square1));
		assertTrue(dungeon.isSquareReachable(square1, square3));
		assertFalse(dungeon.isSquareReachable(square2, square3));
		assertEquals(3,dungeon.getAllReachableSquares(square1).size());
		assertEquals(1,dungeon.getAllReachableSquares(square2).size());
	}
	
	@Test
	public void isSquareReachable_multipleDungeons(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		Shaft<TransparentSquare> shaft = new Shaft<TransparentSquare>(Direction.NORTH,5);
		shaft.addSquare(transparentSquare1, new Coordinate(0,1,0));
		shaft.addSquare(transparentSquare2, new Coordinate(0,2,0));
		shaft.addSquare(transparentSquare3, new Coordinate(0,3,0));
		shaft.addSquare(transparentSquare4, new Coordinate(0,4,0));
		assertFalse(dungeon.isSquareReachable(square2, transparentSquare4));
		square1.setTeleportationDestination(transparentSquare1);
		assertTrue(dungeon.isSquareReachable(square2, transparentSquare4));
	}
	
	@Test
	public void isSquareReachable_doorOpens(){
		dungeon.addSquare(square1, new Coordinate(0,1,0));
		dungeon.addSquare(square2, new Coordinate(0,2,0));
		dungeon.addSquare(transparentSquare2, new Coordinate(1,0,0));
		dungeon.addSquare(transparentSquare3, new Coordinate(1,1,0));
		dungeon.addSquare(transparentSquare4, new Coordinate(1,2,0));
		dungeon.addSquare(transparentSquare5, new Coordinate(1,3,0));
		transparentSquare5.setTeleportationDestination(square3);
		dungeon.addSquare(square3, new Coordinate(4,4,0));
		assertFalse(dungeon.isSquareReachable(square3, square1));
		assertFalse(dungeon.isSquareReachable(square2, square3));
		assertFalse(dungeon.isSquareReachable(square2, transparentSquare2));
		((Door)square1.getBorder(Direction.EAST)).openDoor();
		assertFalse(dungeon.isSquareReachable(square3, square1));
		assertTrue(dungeon.isSquareReachable(square2, square3));
		assertTrue(dungeon.isSquareReachable(square2, transparentSquare2));
	}
	
	@Test
	public void isSquareReachable_differentLevels_closeDoor(){
		dungeon.addSquare(square1, new Coordinate(0,1,1));
		dungeon.addSquare(square2, new Coordinate(0,2,1));
		((Door)transparentSquare2.getBorder(Direction.WEST)).openDoor();
		dungeon.addSquare(transparentSquare2, new Coordinate(1, 2, 1));
		dungeon.addSquare(square4, new Coordinate(1, 2, 0));
		dungeon.addSquare(transparentSquare3, new Coordinate(2, 2, 0));
		square1.setTeleportationDestination(square3);
		((Door)transparentSquare3.getBorder(Direction.WEST)).openDoor();
		assertTrue(dungeon.isSquareReachable(transparentSquare3, square3));
		((Door)transparentSquare3.getBorder(Direction.WEST)).closeDoor();
		assertFalse(dungeon.isSquareReachable(transparentSquare3, square3));
		assertTrue(dungeon.isSquareReachable(square2, square3));
		assertTrue(dungeon.isSquareReachable(square2, transparentSquare2));
	}
	
	@Test
	public void main(){
		System.out.println("");
		main.Main.main(null);
		System.out.println("");
	}	
}
